#include "SQLInListExpr.h"

#include "SQLCharExpr.h"
#include "SQLBooleanExpr.h"
#include "../../../utils/ifeq.h"
#include "../../visitor/SQLASTVisitor.h"

long SQLInListExpr::serialVersionUID = 1L;

SETCALSS(SQLInListExpr);

SQLInListExpr::SQLInListExpr()
{
  targetList = std::make_shared<std::list<SQLExpr_ptr>>();
}

SQLInListExpr::SQLInListExpr(SQLExpr_ptr expr)
{
  targetList = std::make_shared<std::list<SQLExpr_ptr>>();
  this->setExpr(expr);
}

SQLInListExpr::SQLInListExpr(string_ptr expr, string_list_ptr values)
{
  targetList = std::make_shared<std::list<SQLExpr_ptr>>();
  this->setExpr(SQLUtils::toSQLExpr(expr));

  for (string_ptr value : *values)
  {
    targetList->push_back(SQLCharExpr_ptr(new SQLCharExpr(value)));
  }
}

SQLInListExpr::SQLInListExpr(SQLExpr_ptr expr, BOOL_ptr Xnot)
{
  targetList = std::make_shared<std::list<SQLExpr_ptr>>();
  this->setExpr(expr);
  this->Xnot = Xnot;
}

SQLObject_ptr SQLInListExpr::clone()
{
  SQLObject_ptr clone_tmp = SQLInListExpr_ptr(new SQLInListExpr());
  SQLInListExpr_ptr x = std::dynamic_pointer_cast<SQLInListExpr>(clone_tmp);
  // SQLInListExpr *x = new SQLInListExpr();
  x->Xnot = Xnot;
  if (expr != NULL)
  {
    x->setExpr(std::dynamic_pointer_cast<SQLExpr>(expr->clone()));
  }
  for (SQLExpr_ptr e : *targetList)
  {
    SQLExpr_ptr e2 = std::dynamic_pointer_cast<SQLExpr>(e->clone());
    e2->setParent(x);
    x->targetList->push_back(e2);
  }
  return x;
}

BOOL_ptr SQLInListExpr::isNot()
{
  return this->Xnot;
}

void SQLInListExpr::setNot(BOOL_ptr Xnot)
{
  this->Xnot = Xnot;
}

SQLExpr_ptr SQLInListExpr::getExpr()
{
  return this->expr;
}

void SQLInListExpr::setExpr(SQLExpr_ptr expr)
{
  if (expr != NULL)
  {
    expr->setParent(SharedObject(SQLInListExpr));
  }

  this->expr = expr;
}

void SQLInListExpr::addTarget(SQLExpr_ptr x)
{
  x->setParent(SharedObject(SQLInListExpr));
  targetList->push_back(x);
}

void SQLInListExpr::addTarget(int index, SQLExpr_ptr x)
{
  x->setParent(SharedObject(SQLInListExpr));
  std::list<SQLExpr_ptr>::iterator it = targetList->begin();
  std::advance(it, index);
  targetList->insert(it, x);
}

SQLExpr_list_ptr SQLInListExpr::getTargetList()
{
  return this->targetList;
}

BOOL_ptr SQLInListExpr::sortTargetList()
{
  if (targetList->size() < 2)
  {
    return BOOL::TRUE;
  }

  std::list<SQLExpr_ptr>::iterator it = targetList->begin();
  std::advance(it, 0); // 迭代器前进i个元素，注意i是从0开始
  string_ptr firstClass = (*it)->getClass();
  for (int i = 1; i < targetList->size(); i++)
  {
    std::advance(it, i);
    if (firstClass != (*it)->getClass())
    {
      return BOOL::FALSE;
    }
  }

  // if (!Comparable->class->isAssignableFrom(firstClass)) {
  //     return BOOL::FALSE;
  // }

  targetList->sort();
  return BOOL::TRUE;
}

void SQLInListExpr::setTargetList(SQLExpr_list_ptr targetList)
{
  this->targetList.swap(targetList);
}

void SQLInListExpr::accept0(SQLASTVisitor_ptr visitor)
{
  if (visitor->visit(SharedObject(SQLInListExpr)))
  {
    if (expr != NULL)
    {
      expr->accept(visitor);
    }

    if (!targetList->empty())
    {
      for (SQLExpr_ptr item : *targetList)
      {
        if (item != NULL)
        {
          item->accept(visitor);
        }
      }
    }
  }

  visitor->endVisit(SharedObject(SQLInListExpr));
}

SQLObject_list_ptr SQLInListExpr::getChildren()
{
  SQLObject_list_ptr children = std::make_shared<std::list<SQLObject_ptr>>();
  if (this->expr != NULL)
  {
    children->push_back(std::dynamic_pointer_cast<SQLObject>(this->expr));
  }
  // children.splice(children.end(), this->targetList);
  for (auto tmp : *this->targetList)
  {
    children->push_back(std::dynamic_pointer_cast<SQLObject>(tmp));
  }

  return children;
}

int SQLInListExpr::hashCode()
{
  int prime = 31;
  int result = 1;
  result = prime * result + ((expr == NULL) ? 0 : expr->hashCode());
  result = prime * result + (Xnot ? 1231 : 1237);
  // result = prime * result + ((targetList == NULL) ? 0 : targetList->hashCode());
  return result;
}

bool SQLInListExpr::equals(Object_ptr obj)
{
  if (this == obj.get())
  {
    return true;
  }
  if (obj == NULL)
  {
    return false;
  }
  if (getClass() != obj->getClass())
  {
    return false;
  }
  SQLInListExpr_ptr other = std::dynamic_pointer_cast<SQLInListExpr>(obj);
  if (expr == NULL)
  {
    if (other->expr != NULL)
    {
      return false;
    }
    // } else if (!expr == other->expr) {
    //     return BOOL::FALSE;
  }
  if (Xnot != other->Xnot)
  {
    return false;
  }
  if (targetList->empty())
  {
    if (!other->targetList->empty())
    {
      return false;
    }
  }
  else if (!(targetList == other->targetList))
  {
    return false;
  }
  return true;
}

SQLDataType_ptr SQLInListExpr::computeDataType()
{
  return SQLBooleanExpr::DATA_TYPE;
}

BOOL_ptr SQLInListExpr::replace(SQLExpr_ptr expr, SQLExpr_ptr target)
{
  if (this->expr == expr)
  {
    setExpr(target);
    return BOOL::TRUE;
  }
  std::list<SQLExpr_ptr>::iterator it = targetList->begin();
  for (int i = targetList->size() - 1; i >= 0; i--)
  {
    std::advance(it, i);
    if ((*it) == expr)
    {
      if (target == NULL && it != targetList->end())
      {
        targetList->erase(it);
      }
      else
      {
        targetList->insert(it, target);
        target->setParent(SharedObject(SQLInListExpr));
      }
      return BOOL::TRUE;
    }
  }

  return BOOL::FALSE;
}

SQLCommentHint *SQLInListExpr::getHint()
{
  return hint;
}

void SQLInListExpr::setHint(SQLCommentHint *hint)
{
  this->hint = hint;
}
